package com.fw.service.formvalidator.impl;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.fw.service.bean.BaseBean;
import com.fw.service.formvalidator.FormValidatorService;
import com.fw.service.persistence.dto.Error;
import com.fw.service.property.PropertyService;

public class FormValidatorServiceImpl implements FormValidatorService {

    private static Logger log = Logger.getLogger(FormValidatorServiceImpl.class.getName());

    @Autowired
    private PropertyService propertyService;

    @Override
    public Boolean validateLength(String value, int length) {
	Boolean result = Boolean.FALSE;
	if (value != null) {
	    if (value.length() <= length) {
		result = Boolean.TRUE;
	    }
	}
	return result;
    }

    @Override
    public Boolean validateMandatory(Object value) {
	Boolean result = Boolean.FALSE;
	if (value != null) {
	    if (value instanceof String) {
		String string = (String) value;
		if (string != null && !string.isEmpty() && string.trim().length() != 0) {
		    result = Boolean.TRUE;
		}
	    } else {
		result = Boolean.TRUE;
	    }
	}
	return result;
    }

    @Override
    public Boolean validateAlphabetTH(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_ALPHABET_TH);
    }

    @Override
    public Boolean validateAlphabetEN(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_ALPHABET_EN);
    }

    @Override
    public Boolean validateAlphaMericTH(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_ALPHA_MERIC_TH);
    }

    @Override
    public Boolean validateAlphaMericEN(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_ALPHA_MERIC_EN);
    }

    @Override
    public Boolean validateFromTo(Date dateFrom, Date dateTo) {
	Boolean result = Boolean.FALSE;
	if (dateFrom != null && dateTo != null) {
	    if (dateFrom.before(dateTo) || dateFrom.equals(dateTo)) {
		result = Boolean.TRUE;
	    }
	}
	return result;
    }

    @Override
    public Boolean validateMatch(String expected, String actual) {
	Boolean result = Boolean.FALSE;
	if (expected != null && actual != null) {
	    if (expected.equals(actual)) {
		result = Boolean.TRUE;
	    }
	}
	return result;
    }

    @Override
    public Boolean validatePattern(String value, String pattern) {
	Boolean result = Boolean.FALSE;
	if (value != null && !value.isEmpty()) {
	    result = Pattern.matches(pattern, value);
	} else {
	    result = Boolean.TRUE;
	}
	return result;
    }

    @Override
    public Boolean validateMin(Object value, Object minValue) {
	Boolean result = Boolean.FALSE;
	if (value != null && minValue != null) {
	    BigDecimal minVal = new BigDecimal(minValue.toString());
	    if (value instanceof Integer) {
		Integer integerValue = (Integer) value;
		if (integerValue.intValue() >= minVal.intValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Float) {
		Float floatValue = (Float) value;
		if (floatValue.floatValue() >= minVal.floatValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Long) {
		Long longValue = (Long) value;
		if (longValue.longValue() >= minVal.longValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Double) {
		Double doubleValue = (Double) value;
		if (doubleValue.doubleValue() >= minVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof BigDecimal) {
		BigDecimal bigDecimalValue = (BigDecimal) value;
		if (bigDecimalValue.doubleValue() >= minVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    } else {
		BigDecimal bigDecimalValue = new BigDecimal(value.toString());
		if (bigDecimalValue.doubleValue() >= minVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    }
	}
	return result;
    }

    @Override
    public Boolean validateMax(Object value, Object maxValue) {
	Boolean result = Boolean.FALSE;
	if (value != null && maxValue != null) {
	    BigDecimal maxVal = new BigDecimal(maxValue.toString());
	    if (value instanceof Integer) {
		Integer integerValue = (Integer) value;
		if (integerValue.intValue() <= maxVal.intValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Float) {
		Float floatValue = (Float) value;
		if (floatValue.floatValue() <= maxVal.floatValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Long) {
		Long longValue = (Long) value;
		if (longValue.longValue() <= maxVal.longValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof Double) {
		Double doubleValue = (Double) value;
		if (doubleValue.doubleValue() <= maxVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    } else if (value instanceof BigDecimal) {
		BigDecimal bigDecimalValue = (BigDecimal) value;
		if (bigDecimalValue.doubleValue() <= maxVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    } else {
		BigDecimal bigDecimalValue = new BigDecimal(value.toString());
		if (bigDecimalValue.doubleValue() <= maxVal.doubleValue()) {
		    result = Boolean.TRUE;
		}
	    }
	}
	return result;
    }

    @Override
    public List<Error> validate(String clazz, String method, BaseBean baseBean) {
	List<Error> errors = new ArrayList<Error>();
	try {
	    if (clazz != null && method != null && baseBean != null) {
		String className = baseBean.getClass().getName();
		log.debug("Class Name: " + className);
		Field[] fields = baseBean.getClass().getDeclaredFields();
		for (Field field : fields) {
		    field.setAccessible(true);
		    String name = field.getName();
		    Object value = field.get(baseBean);
		    log.debug("Field Name: " + field.getName());
		    log.debug("Field Type: " + field.getType());
		    log.debug("Field Value: " + field.get(baseBean));
		    String propertyKey = "validator." + clazz + "." + method + "." + className + "." + name;
		    String validatorStr = propertyService.getProperty(propertyKey);
		    if (validatorStr != null && !validatorStr.isEmpty()) {
			String[] validatorArray = validatorStr.split(",");
			if (validatorArray != null) {
			    for (String validator : validatorArray) {
				validator = validator.trim();
				Boolean valid = null;
				String errorCode = null;
				String[] errorParameters = null;
				if ("mandatory".equals(validator)) {
				    valid = this.validateMandatory(value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.required";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("length".equals(validator) && value != null) {
				    valid = this.validateLength((String) value,
					    Integer.parseInt(propertyService.getPropertyWithDefaultValue(propertyKey + ".length", "0")));
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.maxlength";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("alphabetTH".equals(validator) && value != null) {
				    valid = this.validateAlphabetTH((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.alphabetTH";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("alphabetEN".equals(validator) && value != null) {
				    valid = this.validateAlphabetEN((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.alphabetEN";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("alphaMericTH".equals(validator) && value != null) {
				    valid = this.validateAlphaMericTH((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.alphaMericTH";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("alphaMericEN".equals(validator) && value != null) {
				    valid = this.validateAlphaMericEN((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.alphaMericEN";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("fromTo".equals(validator) && value != null) {
				    String parameter = propertyService.getProperty(propertyKey + ".fromTo");
				    if (parameter != null) {
					Field toField = baseBean.getClass().getDeclaredField(parameter);
					toField.setAccessible(true);
					valid = this.validateFromTo((Date) value, (Date) toField.get(baseBean));
					if (!valid.booleanValue()) {
					    errorCode = "errors.validator.dateFromTo";
					    errorParameters = new String[] { className + "." + name, className + "." + toField.getName() };
					}
				    }
				} else if ("match".equals(validator) && value != null) {
				    String parameter = propertyService.getProperty(propertyKey + ".match");
				    if (parameter != null) {
					Field matchField = baseBean.getClass().getDeclaredField(parameter);
					matchField.setAccessible(true);
					valid = this.validateMatch((String) value, (String) matchField.get(baseBean));
					if (!valid.booleanValue()) {
					    errorCode = "errors.validator.match";
					    errorParameters = new String[] { className + "." + matchField.getName() };
					}
				    }
				} else if ("pattern".equals(validator) && value != null) {
				    String parameter = propertyService.getProperty(propertyKey + ".pattern");
				    if (parameter != null) {
					valid = this.validatePattern((String) value, parameter);
					if (!valid.booleanValue()) {
					    errorCode = "errors.validator.pattern";
					    errorParameters = new String[] { className + "." + name, parameter };
					}
				    }
				} else if ("min".equals(validator) && value != null) {
				    Object parameter = propertyService.getProperty(propertyKey + ".min");
				    if (parameter != null) {
					valid = this.validateMin(value, parameter);
					if (!valid.booleanValue()) {
					    errorCode = "errors.validator.min";
					    errorParameters = new String[] { className + "." + name, (String) parameter };
					}
				    }
				} else if ("max".equals(validator) && value != null) {
				    Object parameter = propertyService.getProperty(propertyKey + ".max");
				    if (parameter != null) {
					valid = this.validateMax(value, parameter);
					if (!valid.booleanValue()) {
					    errorCode = "errors.validator.max";
					    errorParameters = new String[] { className + "." + name, (String) parameter };
					}
				    }
				} else if ("nameEN".equals(validator) && value != null) {
				    valid = this.validateNameEN((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.nameEN";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("nameTH".equals(validator) && value != null) {
				    valid = this.validateNameTH((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.nameTH";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("email".equals(validator) && value != null) {
				    valid = this.validateEmail((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.email";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("emails".equals(validator) && value != null) {
				    valid = this.validateEmails((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.emails";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("url".equals(validator) && value != null) {
				    valid = this.validateUrl((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.url";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("percent".equals(validator) && value != null) {
				    valid = this.validatePercent((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.percent";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("number".equals(validator) && value != null) {
				    valid = this.validateNumber((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.number";
					errorParameters = new String[] { className + "." + name };
				    }
				} else if ("limitNumber".equals(validator) && value != null) {
				    valid = this.validateLimitNumber((String) value);
				    if (!valid.booleanValue()) {
					errorCode = "errors.validator.limitNumber";
					errorParameters = new String[] { className + "." + name };
				    }
				} else {
				    if (validator.startsWith("if:")) {
					String ifParameter = propertyService.getProperty(propertyKey + ".if");
					Field matchField = baseBean.getClass().getDeclaredField(ifParameter);
					matchField.setAccessible(true);
					if (Boolean.TRUE.equals(((Boolean) matchField.get(baseBean)))) {
					    validator = validator.replaceAll("if:", "");
					    if ("mandatory".equals(validator)) {
						valid = this.validateMandatory(value);
						if (!valid.booleanValue()) {
						    errorCode = "errors.validator.required";
						    errorParameters = new String[] { className + "." + name };
						}
					    } else if ("pattern".equals(validator) && value != null) {
						String parameter = propertyService.getProperty(propertyKey + ".pattern");
						if (parameter != null) {
						    valid = this.validatePattern((String) value, parameter);
						    if (!valid.booleanValue()) {
							errorCode = "errors.validator.pattern";
							errorParameters = new String[] { className + "." + name, parameter };
						    }
						}
					    } else if ("length".equals(validator) && value != null) {
						valid = this.validateLength(
							(String) value,
							Integer.parseInt(propertyService.getPropertyWithDefaultValue(propertyKey
								+ ".length", "0")));
						if (!valid.booleanValue()) {
						    errorCode = "errors.validator.maxlength";
						    errorParameters = new String[] { className + "." + name };
						}
					    }
					}
				    }
				}
				if (errorCode != null) {
				    Error error = new Error();
				    error.setErrorCode(errorCode);
				    error.setErrorParameters(errorParameters);
				    errors.add(error);
				    break;
				}
			    }
			}
		    }
		}
	    }
	} catch (Exception e) {
	    log.error(e, e.getCause());
	    e.printStackTrace();
	}
	return errors;
    }

    @Override
    public Boolean validateNameEN(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_NAME_EN);
    }

    @Override
    public Boolean validateNameTH(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_NAME_TH);
    }

    @Override
    public Boolean validateEmail(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_EMAIL);
    }

    @Override
    public Boolean validateEmails(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_EMAILS);
    }

    @Override
    public Boolean validateUrl(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_URL);
    }

    @Override
    public Boolean validatePercent(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_PERCENT);
    }

    @Override
    public Boolean validateNumber(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_NUMBER);
    }

    @Override
    public Boolean validateLimitNumber(String value) {
	return this.validatePattern(value, FormValidatorService.PATTERN_LIMIT_NUMBER);
    }
}
